﻿#region license

/* 
    -------------------------------------------------------------------------------
                     Copyright 2013 Gabriele Gindro 
    -------------------------------------------------------------------------------
  
  	Versione libreria: 1.2
  	
  	-------------------------------------------------------------------------------
  	
  	Change 1.2:
  	
 	- (Fix): Quando il file ini veniva caricato se esistevano più di due caratteri separatori il valore della chiave
			 corrispondeva solo al primo elemento dell'array. 
  	
  	Dipendenze:
  	
 	- Microsoft .NET Framework versione 4.0 o successiva
 	
 	- Sistemi x86 e x64
	
  	Questo programma è stato creato con SharpDevelop 4.3.1.9430-406354be
  	
    -------------------------------------------------------------------------------
    
 */

#endregion

#region using

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

#endregion

namespace IniManager
{
	
	#region Section
	
	/// <summary>
	/// Classe Section
	/// </summary>
	public class Section
	{
		/// <summary>
		/// Nome della sezione
		/// </summary>
		public string Name { get; set; }
	}
	
	#endregion
	
	#region Key
	
	/// <summary>
	/// Classe Key
	/// </summary>
	public class Key
	{
		/// <summary>
		/// Nome della chiave
		/// </summary>
		public string Name { get; set; }
		
		/// <summary>
		/// Valore della chiave
		/// </summary>
		public string Value { get; set; }
		
	}
	
	#endregion
	
	#region IniManagement
	
	/// <summary>
	/// IniManagement è una libreria scritta in C# per la gestione di un file INI.
	/// </summary>
	public class IniManagement
	{
		#region variables
		
		private Dictionary<Section, Key> content;
		
		private string path;
		private string temp_path;
		
		/// <summary>
		/// Parole chiave
		/// </summary>
		private struct keywords
		{
			public static char openSection = '[';
			public static char closeSection = ']';
			public static char separator = '=';
		}
		
		#endregion
		
		#region methods
	
		/// <summary>
		/// Costruttore
		/// </summary>
		/// <param name="inipath">Percorso del file ini</param>
		public IniManagement(string inipath)
		{
			path = inipath;
			temp_path = null;
			content = new Dictionary<Section, Key>();
		}
		
		/// <summary>
		/// Rimuovi una sezione
		/// </summary>
		/// <param name="section">Nome della sezione da rimuovere</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool RemoveSection(string section)
		{
			if (SectionExists(section))
			{
				Dictionary<Section, Key> temp = new Dictionary<Section, Key>();
				foreach (KeyValuePair<Section, Key> elem in content)
				{
					if (elem.Key.Name != section)
						temp.Add(new Section() { Name = elem.Key.Name }, new Key() { Name = elem.Value.Name, Value = elem.Value.Value });
				}
				content.Clear();
				content = temp;
				return true;
			}
			return false;
		}
		
		/// <summary>
		/// Ottieni la lista dei nomi delle sezioni
		/// </summary>
		/// <returns>Lista dei nomi delle sezioni, null se il file non contiene sezioni</returns>
		public List<Section> GetSections()
		{
			List<Section> sections = new List<Section>();
			List<string> sections_name = new List<string>();
			foreach (Section s in content.Keys)
			{
				if (!sections_name.Contains(s.Name))
				{
					sections.Add(s);
					sections_name.Add(s.Name);
				}
			}
			return (sections.Count > 0) ? sections : null;	
		}
		
		/// <summary>
		/// Rinomina una sezione
		/// </summary>
		/// <param name="section">Nome della sezione da rinominare</param>
		/// <param name="name">Nuovo nome da assegnare</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool SetSection(string section, string name)
		{
			if (SectionExists(section) && !SectionExists(name))
			{
				Dictionary<Section, Key> temp = new Dictionary<Section, Key>();
				foreach (KeyValuePair<Section, Key> elem in content)
				{
					if (elem.Key.Name.Equals(section))
						temp.Add(new Section() { Name = name }, elem.Value);
					else
						temp.Add(elem.Key, elem.Value);
				}
				content.Clear();
				content = temp;
				return true;
			}
			return false;
		}
		
		/// <summary>
		/// Verifica se una sezione è presente o meno sul file
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <returns>true se esiste, false altrimenti</returns>
		public bool SectionExists(string section)
		{
			foreach (Section s in content.Keys)
			{
				if (s.Name.Equals(section))
					return true;
			}
			return false;
		}
		
		/// <summary>
		/// Verifica se una chiave è presente o meno nella sezione specificata del file
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <param name="key">Nome della chiave</param>
		/// <returns>true se esiste, false altriementi</returns>
		public bool KeyExists(string section, string key)
		{
			foreach (KeyValuePair<Section, Key> k in content)
			{
				if (k.Key.Name.Equals(section) && k.Value.Name.Equals(key))
					return true;
			}
			return false;
		}
		
		/// <summary>
		/// Aggiunge una chiave alla sezione specificata
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <param name="key">Nome della chiave</param>
		/// <param name="val">Valore della chiave</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool AddKey(string section, string key, string val)
		{
			if (!KeyExists(section, key))
			{
				content.Add(new Section() { Name = section }, new Key() { Name = key, Value = val });
				return true;
			}
			return false;
		}
		
		/// <summary>
		/// Aggiunge due o più chiavi alla sezione specificata
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <param name="key">Lista di chiavi da aggiungere</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool AddKeys(string section, List<Key> keys)
		{
			bool res = false;
			foreach (Key k in keys)
			{
				if (!KeyExists(section, k.Name))
				{
					content.Add(new Section() { Name = section }, k);
					res = true;
				}
			}
			return res;
		}
		
		/// <summary>
		/// Rimuove una chiave dalla sezione specificata
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <param name="key">Nome della chiave da rimuovere</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool RemoveKey(string section, string key)
		{
			if (KeyExists(section, key))
			{
			   	Section s = null;
			   	foreach (KeyValuePair<Section, Key> elem in content)
			   	{
			   		if (elem.Key.Name.Equals(section) && elem.Value.Name.Equals(key))
			   		{
			   			s = elem.Key;
			   			break;
			   		}
			   	}
			   	if (s != null)
			   	{
					content.Remove(s);
			   		return true;
			   	}
			}
			return false;
		}
		
		/// <summary>
		/// Ottiene la lista delle chiavi presenti nella sezione specificata
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <returns>Lista di chiavi appartenenti alla sezione specificata, null se non esiste la sezione specificata</returns>
		public List<Key> GetKeys(string section)
		{
			if (!SectionExists(section))
				return null;
			List<Key> keys = new List<Key>();
			foreach (KeyValuePair<Section, Key> elem in content)
			{
				if (elem.Key.Name.Equals(section))
					keys.Add(elem.Value);
			}
			return keys;
		}
		
		/// <summary>
		/// Ottiene il valore della chiave della sezione specificata
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <param name="key">Nome della chiave</param>
		/// <returns>Valore della chiave, null se la chiave non esiste</returns>
		public string GetKeyValue(string section, string key)
		{
			if (!SectionExists(section))
				return null;
			foreach (KeyValuePair<Section, Key> elem in content)
			{
				if (elem.Key.Name.Equals(section) && elem.Value.Name.Equals(key))
					return elem.Value.Value;
			}
			return null;
		}
		
		/// <summary>
		/// Modifica il valore della chiave nella sezione specificata
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <param name="key">Nome della chiave</param>
		/// <param name="val">Valore della chiave da modificare</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool SetKeyValue(string section, string key, string val)
		{
			if (KeyExists(section, key))
			{
				Dictionary<Section, Key> temp = new Dictionary<Section, Key>();
				foreach (KeyValuePair<Section, Key> elem in content)
				{
					if (elem.Key.Name.Equals(section) && elem.Value.Name.Equals(key))
						temp.Add(elem.Key, new Key() { Name = key, Value = val });
					else
						temp.Add(elem.Key, elem.Value);
				}
				content.Clear();
				content = temp;
				return true;
			}
			return false;
		}
		
		/// <summary>
		/// Modifica il nome della chiave nella sezione specificata
		/// </summary>
		/// <param name="section">Nome della sezione</param>
		/// <param name="key">Nome della chiave da modificare</param>
		/// <param name="name">Nuovo nome da assegnare alla chiave</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool SetKeyName(string section, string key, string name)
		{
			if (KeyExists(section, key))
			{
				Dictionary<Section, Key> temp = new Dictionary<Section, Key>();
				foreach (KeyValuePair<Section, Key> elem in content)
				{
					if (elem.Key.Name.Equals(section) && elem.Value.Name.Equals(key))
						temp.Add(elem.Key, new Key() { Name = name, Value = elem.Value.Value });
					else
						temp.Add(elem.Key, elem.Value);
				}
				content = temp;
				return true;
			}
			return false;
		}
		
		/// <summary>
		/// Carica il file ini
		/// </summary>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool Load()
		{
			string inipath = (temp_path != null) ? temp_path : path;
			if (!Exists(inipath) || File.ReadAllLines(inipath).LongLength == 0)
				return false;
			content.Clear();
			List<string> sections = new List<string>();
			try
			{
				foreach (string line in File.ReadLines(inipath))
				{
					if (line.Equals(""))
						continue;
					if (line[0].Equals(keywords.openSection) && line[line.Length-1].Equals(keywords.closeSection))
					{
						string section = line.Remove(0,1);
						sections.Add(section.Remove(section.Length-1));
					}
					if (!line[0].Equals(keywords.openSection) && line.Contains(keywords.separator))
					{
						string[] values = line.Split(new char[] { keywords.separator }, StringSplitOptions.RemoveEmptyEntries);
						string val = "";
						for (int i = 1; i < values.Length; i++)
							val += values[i];
						AddKey(sections[sections.Count-1], values[0], val);
					}
				}
			}
			catch (IOException) 
			{
				content.Clear();
				return false; 
			}
			return true;
		}
		
		/// <summary>
		/// Salva tutte le modifiche nel file ini
		/// </summary>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool Save()
		{
			try
			{
				if (!Exists(path))
					File.CreateText(path).Dispose();
				List<string> sections = new List<string>();
				string data = "";
				foreach (Section s in content.Keys)
				{
					if (!sections.Contains(s.Name))
					    sections.Add(s.Name);
				}	
				int len = sections.Count;
				for (int i = 0; i < len; i++)
				{
					data += keywords.openSection + sections[i] + keywords.closeSection + Environment.NewLine;
					foreach (KeyValuePair<Section, Key> elem in content)
					{
						if (elem.Key.Name.Equals(sections[i]))
							data += elem.Value.Name + keywords.separator + elem.Value.Value + Environment.NewLine;
					}
					data += Environment.NewLine;
				}
				data = data.Remove(data.Length-3);
				using (StreamWriter writer = new StreamWriter(path)) { writer.Write(data); }
			}
			catch (IOException)
			{
				return false;
			}
			return true;
		}
		
		/// <summary>
		/// Verifica se il file ini esiste oppure no
		/// </summary>
		/// <param name="inipath"></param>
		/// <returns></returns>
		public bool Exists(string inipath) { return File.Exists(inipath); }
		
		/// <summary>
		/// Consente di cambiare il riferimento ad un'altro file ini
		/// </summary>
		/// <param name="inipath">Nuovo percorso ini</param>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool Change(string inipath)
		{
			if (Exists(inipath))
			{
				this.temp_path = inipath;
				bool res = false;
				if (Load())
				{
					this.path = inipath;
					res = true;
				}
				this.temp_path = null;
				return res;
			}
			return false;
		}
		
		/// <summary>
		/// Elimina tutto il contenuto nel file ini
		/// </summary>
		/// <returns>true se l'operazione è andata a buon fine, false altrimenti</returns>
		public bool Clear()
		{
			try
			{
				if (Exists(path))
				{
					using (StreamWriter writer = new StreamWriter(path)) { writer.Write(""); }
				}
				else
					File.CreateText(path).Dispose();		
			}
			catch (IOException)
			{
				return false;
			}
			content.Clear();
			return true;
		}
	}
	
	#endregion
	
	#endregion

}